/*
 * Copyright:  2018 smarabbit studio.
 *
 * Licensed under the Confluent Community License; you may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 *
 * http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under
 *  the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 *  either express or implied.  See the License for the specific language governing
 *  permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:    2020/6/14 下午2:18
 *
 */

package com.massyframework.beanskin.runtime.service.metadata;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.massyframework.beanskin.Module;
import com.massyframework.beanskin.service.metadata.ClassMetadata;
import com.massyframework.beanskin.service.metadata.ClassMetadataNotFoundException;
import com.massyframework.beanskin.service.metadata.ClassNameDigestNotFoundException;
import com.massyframework.beanskin.service.metadata.MetadataRepository;

/**
 * {@link AbstractMetadataRepository}, 基本实现元数据仓储的基本语义
 */
abstract class AbstractMetadataRepository implements MetadataRepository {

    private Map<String, ClassMetadata> metadataMap;
    private Map<String, ClassMetadata> digestMap;

    /**
     * 构造方法
     */
    public AbstractMetadataRepository() {
        this.metadataMap = new ConcurrentHashMap<>();
        this.digestMap = new ConcurrentHashMap<>();
    }
    
    

    @Override
	public ClassMetadata findClassMetadata(String className) {
		return this.metadataMap.get(className);
	}



	@Override
    public ClassMetadata findClassMetadata(Predicate<ClassMetadata> filter) {
    	return this.metadataMap.values().stream()
    			.filter(filter)
    			.findFirst()
    			.orElse(null);
    }

    @Override
    public ClassMetadata getClassMetadata(Class<?> clazz) {
        Objects.requireNonNull(clazz, "\"clazz\" cannot be null.");

        return this.metadataMap.computeIfAbsent(clazz.getName(), key ->
                this.createClassMetadata(clazz));
    }

    @Override
    public ClassMetadata getClassMetadataWithDigest(String digest) throws ClassNameDigestNotFoundException {
        ClassMetadata result = this.digestMap.get(digest);
        if (result == null){
            throw new ClassNameDigestNotFoundException(digest);
        }
        return result;
    }

    @Override
    public List<ClassMetadata> getClassMetadatas(Predicate<ClassMetadata> filter) {
        return this.metadataMap.values().stream()
                .filter(filter)
                .collect(Collectors.toList());
    }

    @Override
    public List<ClassMetadata> getHierarchyClassMetadatas(Class<?> clazz) {
        List<ClassMetadata> result = new ArrayList<ClassMetadata>();

        Class<?> tmp = clazz;
        do {
            ClassMetadata metadata =
                    this.getClassMetadata(tmp);
            result.add(metadata);
            tmp = tmp.getSuperclass();
        }while (tmp != null && tmp != Object.class);

        return result;
    }

 
    @Override
    public List<ClassMetadata> getHierarchyClassMetadatasWithDigest(String digest) throws ClassMetadataNotFoundException {
        ClassMetadata metadata = this.getClassMetadataWithDigest(digest);
        List<ClassMetadata> result = new ArrayList<>();
        result.add(metadata);

        Class<?> tmp = metadata.getOriginal().getSuperclass();
        while (tmp != null && tmp != Object.class){
            metadata = this.getClassMetadata(tmp);
            result.add(metadata);
            tmp = tmp.getSuperclass();
        }

        return result;
    }

    /**
     * 创建<code>clazz</code>对应的类元数据
     * @param clazz 类型
     * @return {@link ClassMetadata}
     */
    protected ClassMetadata createClassMetadata(Class<?> clazz) {
        ClassMetadata result = new DefaultClassMetadata(clazz);
        String digest = result.getDigest();
        this.digestMap.putIfAbsent(digest, result);
        return result;
    }
    
    /**
     * 清除模块注册的类型
     * @param module {@link Module}
     */
    public void clear(Module<?> module) {
    	ClassLoader classLoader = module.getModuleClassLoader();
    	List<ClassMetadata> founds = this.metadataMap.values().stream()
    			.filter( m-> m.getOriginal().getClassLoader() == classLoader)
    			.collect(Collectors.toList());
    	this.clear(founds);
    }

    /**
     * 清除指定的元数据
     * @param metadatas {@link List}
     */
    protected void clear(List<ClassMetadata> metadatas){
        for (ClassMetadata metadata: metadatas){
            this.metadataMap.remove(metadata.getOriginal().getName());
            this.digestMap.remove(metadata.getDigest());
        }
    }
}
